//School of Informatics Xiamen University, GPL-3.0 license
package cn.edu.xmu.javaee.productdemomybatis.dao;


import cn.edu.xmu.javaee.productdemomybatis.config.ApplicationConfig;
import cn.edu.xmu.javaee.core.exception.BusinessException;
import cn.edu.xmu.javaee.core.model.ReturnNo;
import cn.edu.xmu.javaee.core.model.UserToken;
import cn.edu.xmu.javaee.core.util.JacksonUtil;
import cn.edu.xmu.javaee.productdemomybatis.dao.bo.Product;
import cn.edu.xmu.javaee.productdemomybatis.mapper.generator.GoodsPoMapper;
import cn.edu.xmu.javaee.productdemomybatis.mapper.generator.ProductPoMapper;
import cn.edu.xmu.javaee.productdemomybatis.mapper.generator.po.GoodsPoExample;
import cn.edu.xmu.javaee.productdemomybatis.mapper.generator.po.ProductPo;
import cn.edu.xmu.javaee.productdemomybatis.mapper.generator.po.ProductPoExample;
import cn.edu.xmu.javaee.productdemomybatis.util.CloneFactory;
import cn.edu.xmu.javaee.productdemomybatis.util.Common;
import cn.edu.xmu.javaee.productdemomybatis.util.RequestVariables;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.MessageSource;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Repository;

import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;

import static cn.edu.xmu.javaee.core.model.Constants.PLATFORM;
/**
 * @author Ming Qiu
 **/
@Repository
@Slf4j
@RequiredArgsConstructor
public class ProductDao implements FindProduct,InitializingBean {

    private final ProductPoMapper productPoMapper;
    private final GoodsPoMapper goodsPoMapper;
    private final RequestVariables requestVariables;
    private final ApplicationConfig applicationConfig;
    private final ApplicationContext applicationContext;

    private FindProduct findProduct;

    /**
     * 用名称寻找Product对象
     *
     * @param name 名称
     * @return Product对象列表，带关联的Product返回
     */
    public List<Product> retrieveSimpleProductByName(Integer shopId, String name) throws BusinessException {
        List<Product> productList = new ArrayList<>();
        ProductPoExample example = Common.createExampleByShopIdAndName(shopId, name);
        try {
            List<ProductPo> productPoList = productPoMapper.selectByExample(example);
            for (ProductPo po : productPoList) {
                if (Objects.equals(shopId, po.getShopId()) || PLATFORM.equals(shopId)) {
                    Product product = CloneFactory.copy(new Product(), po);
                    productList.add(product);
                }
            }
            log.debug("retrieveSimpleProductByName: productList = {}", productList);
        } catch (DataAccessException e) {
            log.error(e.getMessage());
            throw new BusinessException(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }

        return productList;
    }

    /**
     * 用id对象找Product对象
     *
     * @param shopId 商铺id
     * @param productId 产品id
     * @return Product对象，不关联的Product
     */
    public Product findSimpleProductById(Integer shopId, Integer productId) throws BusinessException {
        Product product = null;
        try {
            ProductPo productPo = this.findPoById(shopId,productId);
            product = CloneFactory.copy(new Product(), productPo);
            log.debug("retrieveProductByID: product = {}", product);
        } catch (DataAccessException e) {
            log.error(e.getMessage());
            throw new BusinessException(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
        return product;
    }

    /**
     * 创建Product对象
     *
     * @param product 传入的Product对象
     * @return 返回对象ReturnObj
     */
    public Product insert(Product product) throws BusinessException {

        UserToken userToken = this.requestVariables.getUser();
        Product retObj = null;
        try {
            product.setCreator(userToken.getName());
            product.setGmtCreate(LocalDateTime.now());
            ProductPo po = CloneFactory.copy(new ProductPo(), product);
            int ret = this.productPoMapper.insertSelective(po);
            retObj = CloneFactory.copy(new Product(), po);
        } catch (DataAccessException e) {
            log.error(e.getMessage());
            throw new BusinessException(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
        return retObj;
    }

    /**
     * 修改商品信息
     *
     * @param product 传入的product对象
     * @return void
     */
    public void update(Product product) throws BusinessException {
        UserToken userToken = this.requestVariables.getUser();
        try {
            this.findPoById(userToken.getDepartId(), product.getId());
            product.setGmtModified(LocalDateTime.now());
            product.setModifier(userToken.getName());
            ProductPo po = CloneFactory.copy(new ProductPo(), product);
            this.productPoMapper.updateByPrimaryKeySelective(po);
        } catch (DataAccessException e) {
            log.error(e.getMessage());
            throw new BusinessException(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    /**
     * 删除商品，连带规格
     *
     * @param id 商品id
     * @return
     */
    public void delete(Integer id) throws BusinessException {
        UserToken user = this.requestVariables.getUser();
        try {
            this.findPoById(user.getDepartId(), id);

            GoodsPoExample productIdExample = new GoodsPoExample();
            productIdExample.createCriteria().andProductIdEqualTo(id);
            this.goodsPoMapper.deleteByExample(productIdExample);

            GoodsPoExample relateProductIdExample = new GoodsPoExample();
            relateProductIdExample.createCriteria().andRelateProductIdEqualTo(id);
            this.goodsPoMapper.deleteByExample(relateProductIdExample);

            this.productPoMapper.deleteByPrimaryKey(id);

        } catch (DataAccessException e) {
            log.error(e.getMessage());
            throw new BusinessException(ReturnNo.INTERNAL_SERVER_ERR, e.getMessage());
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        String daoBean = this.applicationConfig.getMybatis();
        log.info("the application initialized with {}", daoBean);
        this.findProduct = this.applicationContext.getBean(daoBean, FindProduct.class);
    }

    @Override
    public Product findById(Integer shopId, Integer productId) throws BusinessException {
        return this.findProduct.findById(shopId, productId);
    }

    @Override
    public List<Product> retrieveByName(Integer shopId, String name) throws BusinessException {
        return this.findProduct.retrieveByName(shopId, name);
    }

    private ProductPo findPoById(Integer shopId, Integer productId){
        ProductPo productPo = this.productPoMapper.selectByPrimaryKey(productId);
        if (Objects.isNull(productPo)) {
            String[] objects = new String[] {"${product}", productId.toString()};
            throw new BusinessException(ReturnNo.RESOURCE_ID_NOTEXIST, JacksonUtil.toJson(objects));
        }
        if (!Objects.equals(shopId, productPo.getShopId()) && !PLATFORM.equals(shopId)){
            String[] objects = new String[] {"${product}", productId.toString(), shopId.toString()};
            throw new BusinessException(ReturnNo.RESOURCE_ID_OUTSCOPE, JacksonUtil.toJson(objects));
        }
        return productPo;
    }
}
